package cn.itsub.proxy.server.work.config;

import java.io.Serializable;
import java.util.*;

import cn.itsub.proxy.server.app.entity.Tunnel;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;


@Slf4j
@Data
@Component
public class ProxyConfig implements Serializable {

    private static final long serialVersionUID = 1L;

    /** 代理服务器绑定主机host */
    //@Value("${netoo.server.host:0.0.0.0}")
    //private String serverBind;

    /** 代理服务器与代理客户端通信端口 */
   // @Value("${netoo.server.port:4900}")
    //private Integer serverPort;

    /** 更新配置后保证在其他线程即时生效 */
    private static ProxyConfig instance = null;

    @Autowired
    public void setInstance(ProxyConfig instance) {
        System.out.println("获取config实例");
        ProxyConfig.instance = instance;
    }

    /** 代理服务器为各个代理客户端（key）开启对应的端口列表（value） */
    private volatile Map<String, List<Integer>> clientInetPortMapping = new HashMap<String, List<Integer>>();

    /** 代理服务器上的每个对外端口（key）对应的代理客户端背后的真实服务器信息（value） */
    private volatile Map<Integer, String> inetPortLanInfoMapping = new HashMap<Integer, String>();

    /** 配置变化监听器 */
    private List<ConfigChangedListener> configChangedListeners = new ArrayList<ConfigChangedListener>();

    public JsonArray getMappings(String clientKey) {
        JsonArray arr = new JsonArray();
        for (var en : clientInetPortMapping.entrySet()) {
            if (!StringUtils.isEmpty(clientKey)) {
                if (!en.getKey().equals(clientKey)) {
                    continue;
                }
            }
            JsonObject obj = new JsonObject();
            obj.addProperty("clientKey",en.getKey());
            List<Integer> list = en.getValue();
            JsonArray mappings = new JsonArray();
            en.getValue().forEach(port->{
                String lanInfo = inetPortLanInfoMapping.get(port);
                JsonObject mp = new JsonObject();
                mp.addProperty("port",port);
                mp.addProperty("info",lanInfo);
                mappings.add(mp);
            });
            obj.add("mappings",mappings);
            log.info(obj.toString());
            arr.add(obj);
        }
        return arr;
    }

    /**
     * 设置用户的隧道列表
     * @param clientKey
     * @param list
     */
    public void setClientTunnels(String clientKey, List<Tunnel> list) {
        List<Integer> ports = new ArrayList<>();
        if (list != null) {
            for (Tunnel tunnel : list) {
                if (tunnel.getState() == 0) {
                    continue;
                }
                ports.add(tunnel.getWanPort());
                String val = String.format("%s:%s", tunnel.getLanHost(), tunnel.getLanPort());
                inetPortLanInfoMapping.put(tunnel.getWanPort(), val);
                log.info(String.format("[ %s | %s => %s ]", clientKey,tunnel.getWanPort(),val));
            }
        }

        clientInetPortMapping.put(clientKey, ports);
        notifyconfigChangedListeners();
    }

    private ProxyConfig() {

    }

    //从数据库加载隧道信息
    public void update2() {
        log.info("正在加载隧道信息");
       // log.info("WorkHost {}, WorkPort {}",serverBind, serverPort);

        List<Tunnel> list = null;
        if (list == null) {
            return;
        }

        Map<String, List<Integer>> clientInetPortMapping = new HashMap<String, List<Integer>>();
        Map<Integer, String> inetPortLanInfoMapping = new HashMap<Integer, String>();

        Map<String, List<Tunnel>> map = new HashMap<>();
        list.forEach(t->{
            if (t.getState() == 1) {
                String clientKey = t.getClientKey();
                if (!map.containsKey(clientKey)) {
                    map.put(clientKey, new ArrayList<>());
                }
                map.get(clientKey).add(t);
            }

        });
        for (var en : map.entrySet()) {
            String clientKey = en.getKey();
            List<Integer> ports = new ArrayList<>();
            if (list == null || list.isEmpty()) {
                log.info(String.format("隧道列表为空:[clientKey=%s]", clientKey));
                continue;
            }
            for (Tunnel tunnel : en.getValue()) {
                ports.add(tunnel.getWanPort());
                String val = String.format("%s:%s", tunnel.getLanHost(), tunnel.getLanPort());
                inetPortLanInfoMapping.put(tunnel.getWanPort(), val);
                log.info(String.format("update2[ %s | %s => %s ]", clientKey,tunnel.getWanPort(),val));
            }
            clientInetPortMapping.put(clientKey, ports);
        }

        // 替换之前的配置关系
        this.clientInetPortMapping = clientInetPortMapping;
        this.inetPortLanInfoMapping = inetPortLanInfoMapping;

        log.info("隧道数量---"+this.inetPortLanInfoMapping.size());

        notifyconfigChangedListeners();
    }

    /**
     * 配置更新通知
     */
    private void notifyconfigChangedListeners() {
        List<ConfigChangedListener> changedListeners = new ArrayList<ConfigChangedListener>(configChangedListeners);
        for (ConfigChangedListener changedListener : changedListeners) {
            changedListener.onChanged();
        }
    }

    /**
     * 添加配置变化监听器
     *
     * @param configChangedListener
     */
    public void addConfigChangedListener(ConfigChangedListener configChangedListener) {
        configChangedListeners.add(configChangedListener);
    }

    /**
     * 移除配置变化监听器
     *
     * @param configChangedListener
     */
    public void removeConfigChangedListener(ConfigChangedListener configChangedListener) {
        configChangedListeners.remove(configChangedListener);
    }

    /**
     * 获取代理客户端对应的代理服务器端口
     *
     * @param clientKey
     * @return
     */
    public List<Integer> getClientInetPorts(String clientKey) {
        return clientInetPortMapping.get(clientKey);
    }

    /**
     * 获取所有的clientKey
     *
     * @return
     */
    public Set<String> getClientKeySet() {
        return clientInetPortMapping.keySet();
    }

    /**
     * 根据代理服务器端口获取后端服务器代理信息
     *
     * @param port
     * @return
     */
    public String getLanInfo(Integer port) {
        return inetPortLanInfoMapping.get(port);
    }

    /**
     * 返回需要绑定在代理服务器的端口（用于用户请求）
     *
     * @return
     */
    public List<Integer> getUserPorts() {
        List<Integer> ports = new ArrayList<Integer>();
        Iterator<Integer> ite = inetPortLanInfoMapping.keySet().iterator();
        while (ite.hasNext()) {
            ports.add(ite.next());
        }

        return ports;
    }

    public static ProxyConfig getInstance() {
        return instance;
    }

    /**
     * 代理客户端
     */
    @Data
    @EqualsAndHashCode
    public static class ClientRo implements Serializable {

        /** 客户端备注名称 */
        private String name;

        /** 代理客户端唯一标识key */
        private String clientKey;

        /** 代理客户端与其后面的真实服务器映射关系 */
        private List<ClientProxyMapping> proxyMappings;

        private int status;

    }

    /**
     * 代理客户端与其后面真实服务器映射关系
     */
    @Data
    @EqualsAndHashCode
    public static class ClientProxyMapping {

        /** 代理服务器端口 */
        private Integer inetPort;

        /** 需要代理的网络信息（代理客户端能够访问），格式 192.168.1.99:80 (必须带端口) */
        private String lan;

        /** 备注名称 */
        private String name;

    }

    /**
     * 配置更新回调
     *
     * @author fengfei
     *
     */
    public static interface ConfigChangedListener {

        void onChanged();
    }
}
